package it.tukano.swingdeco.controls;

import it.tukano.fun.Function1;
import it.tukano.fun.Function2;
import it.tukano.swingdeco.ExtUtilities;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;

/**
 * JComboBox extensions
 * @author pgi
 */
public class JComboBoxExt extends JComboBox {

    /* The serial version uid of this class */
    private static final long serialVersionUID = 0;

    public static JComboBoxExt newFromArray(Object[] values) {
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        for (int i = 0; i < values.length; i++) {
            Object object = values[i];
            model.addElement(object);
        }
        JComboBoxExt box = new JComboBoxExt(model);
        return box;
    }
    
    public JComboBoxExt(Object...items) {
        this(new DefaultComboBoxModel(items));
    }

    /**
     * Instance initializer
     */
    public JComboBoxExt(DefaultComboBoxModel model) {
        super(model);
    }
    
    public JComboBoxExt withName(String name) {
        setName(name);
        return this;
    }
    
    /**
     * Set as editable and return this
     */
    public JComboBoxExt editable() {
        setEditable(true);
        return this;
    }
    
    /**
     * Installs a renderer that uses the values as displayes if they are components
     * @return this
     */
    public JComboBoxExt withComponentRenderer() {
        setRenderer(new DefaultListCellRenderer() {
            //Default serial version code
            private static final long serialVersionUID = 0;

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                return value instanceof Component ?
                        (Component) value :
                        super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            }
        });
        return this;
    }

    public JComboBoxExt withSimpleRenderer(final Function2<Object, JLabel, ?> simpleRenderer) {
        DefaultListCellRenderer renderer = new DefaultListCellRenderer() {

            /* Serial version of this class */
            private static final long serialVersionUID = 0;

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                simpleRenderer.apply(value, this);
                return this;
            }
        };
        setRenderer(renderer);
        return this;
    }

    /**
     * Set the tool tip text of this box
     * @param bundle the resource bundle name
     * @param key the resource key
     * @return this
     */
    public JComboBoxExt setTip(String bundle, String key) {
        ExtUtilities.setToolTipText(this, bundle, key);
        return this;
    }

    /**
     * Add a callback to the selection event
     * @param callback the callback (receives the new selected item)
     * @return this
     */
    public JComboBoxExt onSelectionChange(final Function1<Object, ?> callback) {
        addItemListener(new ItemListener() {

            public void itemStateChanged(ItemEvent e) {
                if(e.getStateChange() == ItemEvent.SELECTED) {
                    callback.apply(getSelectedItem());
                }
            }
        });
        return this;
    }

    /**
     * Add a callback invoked when the selected item changes
     * @param runn the callback
     * @return this
     */
    public JComboBoxExt onSelectionChange(final Runnable runn) {
        addItemListener(new ItemListener() {

            public void itemStateChanged(ItemEvent e) {
                if(e.getStateChange() == ItemEvent.SELECTED) {
                    runn.run();
                }
            }
        });
        return this;
    }

    /**
     * Append an item and returns this
     * @param item the item to add
     * @return this
     */
    public JComboBoxExt append(Object item) {
        getModel().addElement(item);
        return this;
    }

    public JComboBoxExt append(Object item, boolean fireEvent) {
        List<ItemListener> listeners = Arrays.asList(getItemListeners());
        if(!fireEvent) for (ItemListener itemListener : listeners) {
            Logger.getLogger(JComboBoxExt.class.getName()).log(Level.INFO, "detach listeners...");
            removeItemListener(itemListener);
        }
        Logger.getLogger(JComboBoxExt.class.getName()).log(Level.INFO, "append");
        append(item);
        Logger.getLogger(JComboBoxExt.class.getName()).log(Level.INFO, "appended");
        if(!fireEvent) for (ItemListener itemListener : listeners) {
            Logger.getLogger(JComboBoxExt.class.getName()).log(Level.INFO, "reattach listeners...");
            addItemListener(itemListener);
        }
        return this;
    }

    /**
     * Set the values of this editor
     * @param values the new values
     * @return this
     */
    public JComboBoxExt setValues(Collection<?> values) {
        setModel(new DefaultComboBoxModel(values.toArray(new Object[values.size()])));
        return this;
    }

    /**
     * Returns the model as an instanceof DefaultComboBoxModel
     * @return the combo box model of this box
     */
    @Override
    public DefaultComboBoxModel getModel() {
        return (DefaultComboBoxModel) super.getModel();
    }

    /** String.valueOf(selected item)   */
    public String getText() {
        return String.valueOf(getSelectedItem());
    }
}
